/*-------------------------------------------------------------------------
 *
 * ts_type.h
 *	  tsvector 和 tsquery 类型的定义
 *
 * Copyright (c) 1998-2022, PostgreSQL Global Development Group
 *
 * src/include/tsearch/ts_type.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef _PG_TSTYPE_H_
#define _PG_TSTYPE_H_

#include "fmgr.h"
#include "utils/memutils.h"


/*
 * TSVector 类型。
 *
 * tsvector 数据类型的结构：
 * 1) 标准 varlena 头部
 * 2) int32		size - 词素数量（WordEntry 数组条目）
 * 3) WordEntry 数组 - 每个词素一个；必须根据
 *				tsCompareString() 排序（即，词素字符串的 memcmp）。
 *				WordEntry->pos 给出从 WordEntry
 *				数组末尾到词素字符串起始的字节数，长度为 len。
 * 4) 每个词素的数据存储：
 *	  词素字符串（不是以 null 结尾）
 *	  如果 haspos 为 true：
 *		必要时填充字节以使位置信息 2 字节对齐
 *		uint16			后续位置的数量
 *		WordEntryPos[]	位置
 *
 * 每个词素的位置必须排序。
 *
 * 注意，tsvectorsend/recv 认为 sizeof(WordEntry) == 4
 */

typedef struct
{
	uint32
				haspos:1,
				len:11,			/* MAX 2Kb */
				pos:20;			/* MAX 1Mb */
} WordEntry;

#define MAXSTRLEN ( (1<<11) - 1)
#define MAXSTRPOS ( (1<<20) - 1)

extern int	compareWordEntryPos(const void *a, const void *b);

/*
 * 相当于
 * typedef struct {
 *		uint16
 *			weight:2,
 *			pos:14;
 * }
 */

typedef uint16 WordEntryPos;

typedef struct
{
	uint16		npos;
	WordEntryPos pos[FLEXIBLE_ARRAY_MEMBER];
} WordEntryPosVector;

/* 具有恰好 1 个条目的 WordEntryPosVector */
typedef struct
{
	uint16		npos;
	WordEntryPos pos[1];
} WordEntryPosVector1;


#define WEP_GETWEIGHT(x)	( (x) >> 14 )
#define WEP_GETPOS(x)		( (x) & 0x3fff )

#define WEP_SETWEIGHT(x,v)	( (x) = ( (v) << 14 ) | ( (x) & 0x3fff ) )
#define WEP_SETPOS(x,v)		( (x) = ( (x) & 0xc000 ) | ( (v) & 0x3fff ) )

#define MAXENTRYPOS (1<<14)
#define MAXNUMPOS	(256)
#define LIMITPOS(x) ( ( (x) >= MAXENTRYPOS ) ? (MAXENTRYPOS-1) : (x) )

/* 此结构表示完整的 tsvector 数据项 */
typedef struct
{
	int32		vl_len_;		/* varlena 头部（请勿直接触摸！） */
	int32		size;
	WordEntry	entries[FLEXIBLE_ARRAY_MEMBER];
	/* 词素在 entries[] 数组之后 */
} TSVectorData;

typedef TSVectorData *TSVector;

#define DATAHDRSIZE (offsetof(TSVectorData, entries))
#define CALCDATASIZE(nentries, lenstr) (DATAHDRSIZE + (nentries) * sizeof(WordEntry) + (lenstr) )

/* 指向 tsvector 的 WordEntry 数组起始的指针 */
#define ARRPTR(x)	( (x)->entries )

/* 指向 tsvector 的词素存储起始的指针 */
#define STRPTR(x)	( (char *) &(x)->entries[(x)->size] )

#define _POSVECPTR(x, e)	((WordEntryPosVector *)(STRPTR(x) + SHORTALIGN((e)->pos + (e)->len)))
#define POSDATALEN(x,e) ( ( (e)->haspos ) ? (_POSVECPTR(x,e)->npos) : 0 )
#define POSDATAPTR(x,e) (_POSVECPTR(x,e)->pos)

/*
 * fmgr 接口宏
 */

#define DatumGetTSVector(X)			((TSVector) PG_DETOAST_DATUM(X))
#define DatumGetTSVectorCopy(X)		((TSVector) PG_DETOAST_DATUM_COPY(X))
#define TSVectorGetDatum(X)			PointerGetDatum(X)
#define PG_GETARG_TSVECTOR(n)		DatumGetTSVector(PG_GETARG_DATUM(n))
#define PG_GETARG_TSVECTOR_COPY(n)	DatumGetTSVectorCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_TSVECTOR(x)		return TSVectorGetDatum(x)


/*
 * TSQuery
 *
 *
 */

typedef int8 QueryItemType;

/* QueryItemType 的有效值： */
#define QI_VAL 1
#define QI_OPR 2
#define QI_VALSTOP 3			/* 这仅在 parse_tsquery 的中间栈
								 * 表示中使用。它在其他地方不是一个
								 * 合法类型。 */

/*
 * QueryItem 是 tsquery 中的一个节点 - 操作符或操作数。
 */
typedef struct
{
	QueryItemType type;			/* 操作数或操作符的种类 (ts_tokentype) */
	uint8		weight;			/* 搜索操作数的权重。这是一个
								 * 允许权重的位掩码。如果它 =0 则
								 * 允许任何权重。权重和位
								 * 映射：A: 1<<3 B: 1<<2 C: 1<<1 D: 1<<0 */
	bool		prefix;			/* 如果是前缀搜索则为真 */
	int32		valcrc;			/* XXX: pg_crc32 将是更合适的
								 * 数据类型，但我们在代码中使用有符号
								 * 整数进行比较。它们也需要被
								 * 更改。 */

	/* 指向操作数的文本值的指针，必须与 WordEntry 相关 */
	uint32
				length:12,
				distance:20;
} QueryOperand;


/*
 * QueryOperator.operator 的合法值。
 */
#define OP_NOT			1
#define OP_AND			2
#define OP_OR			3
#define OP_PHRASE		4		/* 最高代码，tsquery_cleanup.c */
#define OP_COUNT		4

extern PGDLLIMPORT const int tsearch_op_priority[OP_COUNT];

/* 通过其代码获取操作优先级 */
#define OP_PRIORITY(x)	( tsearch_op_priority[(x) - 1] )
/* 获取 QueryOperator 的优先级 */
#define QO_PRIORITY(x)	OP_PRIORITY(((QueryOperator *) (x))->oper)

typedef struct
{
	QueryItemType type;
	int8		oper;			/* 详见上文 */
	int16		distance;		/* OP_PHRASE 的参数之间的距离 */
	uint32		left;			/* 指向左操作数的指针。右操作数是
								 * item + 1，左操作数放置在
								 * item+item->left */
} QueryOperator;

/*
 * 注意：TSQuery 是 4 字节对齐的，因此确保 QueryItem 内部没有
 * 需要 8 字节对齐的字段，例如 int64。
 */
typedef union
{
	QueryItemType type;
	QueryOperator qoperator;
	QueryOperand qoperand;
} QueryItem;

/*
 * 存储：
 *	(len)(size)(QueryItem 数组)(作为 '\0' 结束的操作数 c 字符串)
 */

typedef struct
{
	int32		vl_len_;		/* varlena 头部（请勿直接触摸！） */
	int32		size;			/* number of QueryItems */
	char		data[FLEXIBLE_ARRAY_MEMBER];	/* 数据从这里开始 */
} TSQueryData;

typedef TSQueryData *TSQuery;

#define HDRSIZETQ	( VARHDRSZ + sizeof(int32) )

/* 计算头部和所有 QueryItems 的大小。size 是 QueryItems 的数量，lenofoperand 是所有操作数的总长度 */
#define COMPUTESIZE(size, lenofoperand) ( HDRSIZETQ + (size) * sizeof(QueryItem) + (lenofoperand) )
#define TSQUERY_TOO_BIG(size, lenofoperand) \
	((size) > (MaxAllocSize - HDRSIZETQ - (lenofoperand)) / sizeof(QueryItem))

/* 返回指向 TSQuery 中第一个 QueryItem 的指针 */
#define GETQUERY(x)  ((QueryItem*)( (char*)(x)+HDRSIZETQ ))

/* 返回指向 TSQuery 中操作数开始位置的指针 */
#define GETOPERAND(x)	( (char*)GETQUERY(x) + ((TSQuery)(x))->size * sizeof(QueryItem) )

/*
 * fmgr 接口宏
 * 注意，TSQuery 类型标记为普通存储，因此无法被提前处理
 * 但出于简单性，使用 PG_DETOAST_DATUM_COPY
 */

#define DatumGetTSQuery(X)			((TSQuery) DatumGetPointer(X))
#define DatumGetTSQueryCopy(X)		((TSQuery) PG_DETOAST_DATUM_COPY(X))
#define TSQueryGetDatum(X)			PointerGetDatum(X)
#define PG_GETARG_TSQUERY(n)		DatumGetTSQuery(PG_GETARG_DATUM(n))
#define PG_GETARG_TSQUERY_COPY(n)	DatumGetTSQueryCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_TSQUERY(x)		return TSQueryGetDatum(x)

#endif							/* _PG_TSTYPE_H_ */
